﻿using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BFTest.Particles
{
    abstract class CParticle
    {
        bool alive;
        public bool Alive { get { return alive; } private set { alive = value; } }

        Vector2 position;
        public Vector2 Position { get { return position; } private set { position = value; UpdateBoundingBox(); } }

        Rectangle boundingBox;
        public Rectangle BoundingBox { get { return boundingBox; } private set { boundingBox = value; } }

        Vector2 velocity;
        public Vector2 Velocity { get { return velocity; } private set { velocity = value; } }

        bool fadeOut;
        public bool FadeOut { get { return fadeOut; } private set { fadeOut = value; } }

        TimeSpan? timeToLive;
        public TimeSpan? TimeToLive { get { return timeToLive; } private set { timeToLive = value; } }

        public bool Persistant { get { return !TimeToLive.HasValue; } }

        TimeSpan lifeTime;
        public TimeSpan LifeTime { get { return lifeTime; } private set { lifeTime = value; } }

        float alpha = 1;
        public float Alpha { get { return alpha; } set { alpha = value; } }

        abstract public Color ParticleColor { get; set; }
        abstract public Vector2 Size { get; }
        abstract public Texture2D Texture { get; }

        // TODO: Remove gameTime from the constructor.
        // Is there a way we can get the current game time from inside?
        public CParticle(Vector2 position, Vector2 velocity, TimeSpan? timeToLive, bool fadeOut = false)
        {
            Position = position;
            Velocity = velocity;
            if (timeToLive.HasValue)
                TimeToLive = timeToLive;
            if (!TimeToLive.HasValue && FadeOut == true)
                throw new ArgumentException("fadeOut", "fadeOut cannot be true with persistant particles.");
            else
                FadeOut = fadeOut;
            Alive = true;

            this.Initialize();
        }

        protected virtual void Initialize()
        {

        }

        protected virtual void OnDeath()
        {
            // This method is called whenever the particle dies. 
            // Used in situations like fire particles emitting smoke particles on death.
        }

        public virtual void Update(GameTime gameTime)
        {
            Position += (Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds);

            if (TimeToLive.HasValue)
            {
                LifeTime += gameTime.ElapsedGameTime;
                if (LifeTime >= TimeToLive)
                {
                    Alive = false;
                    OnDeath();
                }
                else
                {
                    if (FadeOut)
                        Alpha = MathHelper.Clamp((1 - (float)(LifeTime.TotalSeconds / TimeToLive.Value.TotalSeconds)), 0, 1);
                }
            }
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Texture, BoundingBox, new Color(ParticleColor.R, ParticleColor.G, ParticleColor.B, Alpha));
        }

        private void UpdateBoundingBox()
        {
            BoundingBox = new Rectangle(
                (int)(Position.X - (Size.X / 2)),
                (int)(Position.Y - (Size.Y / 2)),
                (int)Size.X, (int)Size.Y);
        }
    }
}
